<?php

namespace App\Http\Controllers\App\V1;

use App\Enums\SmsTypeEnum;
use App\Http\Controllers\App\Rebuild\PublicApiController;
use App\Models\MainDB\UserAuthBlacklist;
use App\Repositories\LogRepository;
use App\Services\Tools\SmsService;
use Exception;
use Carbon\Carbon;
use App\Response\Response;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Cache;
use App\Http\Controllers\BaseController;
use Illuminate\Http\Request;        //引用http请求类
use App\Exceptions\BasicException;  //引入框架异常处理类
use Psr\Container\ContainerExceptionInterface;
use Psr\Container\NotFoundExceptionInterface;

class TxyAuthController extends BaseController
{
    private static $access_token_url = 'https://miniprogram-kyc.tencentcloudapi.com/api/oauth2/access_token';// 发送请求地址Access Token 获取
    private static $api_ticket_url = 'https://miniprogram-kyc.tencentcloudapi.com/api/oauth2/api_ticket';
    private static $api_getfaceid_url = 'https://miniprogram-kyc.tencentcloudapi.com/api/server/getfaceid?orderNo=';
    private static $api_queryfacerecord_url = 'https://miniprogram-kyc.tencentcloudapi.com/api/v2/base/queryfacerecord?orderNo=';
    // private static $app_id = 'IDAFTN2Z';
    // private static $password = 'ruPbAKmT0C5oDH2CbKhVwwIuEg2wJABmXzGwem9uw4UkqJSN1pBYm6Pjg9Ic2zx2';
    private $appId;
    private $password;
    private static $txface_accesstoken_cache_key = 'txfaceaccesstoken:' . UID;
    private static $txface_signticket_cache_key = 'txfacesignticket:' . UID;

    public function __construct(Request $request)
    {
        $clientType = $request->header('clientType');
//        $logRtx = new LogRepository("aaaatx-auth-request");
//        $logRtx->log("request1:", $clientType);
//        $logRtx->File();
        $this->appId = env('TX_APP_ID');
        // 为避免私钥随源码泄露，推荐从文件中读取私钥字符串而不是写入源码中
        $this->password = env('TX_SECRET_KEY');
//        $logRtx->log("request2:", $this->appId);
//        $logRtx->File();
//        $logRtx->log("request3:",  $this->password);
//        $logRtx->File();
    }

    // 人脸核身初始化
    public function initialize(Request $request)
    {

        try {
            $certName = $request->input('certName');
            $certNo = $request->input('certNo');
            $code = $request->input('code');
            $logRtx = new LogRepository("tx-auth-request");
            if (!$certName) {
                throw new BasicException(20002);
            }
            if (!checkCertNo($certNo)) {
                throw new BasicException(20003);
            }
            if (!isAdult($certNo)) {
                throw new BasicException(120003);
            }
            if (!app('redis')->exists(PublicApiController::user_blacklist_IdNumber)) {
                PublicApiController::userList("IdNumber");
            }
            $user_blacklist_IdNumber_list = app('redis')->get(PublicApiController::user_blacklist_IdNumber);
            if (in_array($certNo,json_decode($user_blacklist_IdNumber_list,true))){
                throw new BasicException(-1, "已加入实名黑名单");
            }

//            // 验证身份证号是否被拉黑
            $isBan = UserAuthBlacklist::query()->where("id_number", $certNo)->exists();
            if ($isBan) {
                throw new BasicException(-1, "已加入实名黑名单");
            }

            $user_id = UID;
            $mobile = app('repo_user')->getUserMobile($user_id);
            if (!$mobile) {
                throw new BasicException(20009);
            }
            if (!SmsService::verify($mobile, $code, SmsTypeEnum::AUTH_QUICK)) {
                throw new BasicException(20010);
            }
            $detail = app('repo_user_auth')->getUserAuth($user_id);
            if ($detail) {
                if ($detail->app_status == 0) {
                    throw new BasicException(20007);
                } elseif ($detail->app_status == 1) {
                    throw new BasicException(20008);
                }
            }

            //每天限制提交5次
            $key = 'auth:tx:' . $user_id;
            $times = Cache::get($key);
            if ($times >= 5) {
                throw new BasicException(20011);
            }

            $result = self::getFaceId($user_id, ['cert_name' => $certName, 'cert_no' => $certNo]);
            if ($result['code'] == 0 && $result['txres']['code'] == 0) {
                // 成功
                $res = app('model_alipay_auth')->create([
                    'user_id' => $user_id,
                    'order_no' => $result['txparm']['orderNo'],
                    'cert_name' => $certName,
                    'cert_no' => $certNo,
                    'add_time' => time(),
                    'certify_id' => $result['txres']['result']['faceId'],
                ]);
                if (!$res) {
                    throw new BasicException(9001);
                }
                $logRtx->log("请求initialize实名成功返回参数为：", date("Y-m-d H:i:s"));
                $logRtx->log("request", json_encode($result));
                $logRtx->File();
                return Response::sendData([
                    'certify_id' => $result['txres']['result']['faceId'],
                    'nonce' => $result['txparm']['nonce'],
                    'appid' => $result['txparm']['webankAppId'],
                    'orderNo' => $result['txparm']['orderNo'],
                    'apiVersion' => $result['txparm']['version'],
                    'userid' => $result['txparm']['userId'],
                    'sign' => $result['sdksign'],
                    'error_code' => 0
                ]);
            }
            app('redis')->del(self::$txface_signticket_cache_key);
            app('redis')->del(self::$txface_accesstoken_cache_key);
            $logRtx->log("请求initialize实名失败返回参数为：", date("Y-m-d H:i:s"));
            $logRtx->log("request", json_encode($result));
            $logRtx->File();
            throw new BasicException(-1, "实名失败");
        } catch (Exception $e) {
            throw new BasicException(-1, $e->getMessage());
        }
    }

    /**
     * 返回数据
     */
    public function callback(Request $request)
    {
        $logRtx = new LogRepository("tx-auth-request");

        $status = $request->input('status');
        $faceid = $request->input('faceid');
        if (!$faceid) {
            throw new BasicException(20005);
        }
        $order_no = $request->input('order_no');
        if (!$order_no) {
            throw new BasicException(20005);
        }
        $data = app('model_alipay_auth')->where('order_no', $order_no)->first();
        if (!$data) {
            throw new BasicException(20006);
        }
        if ($data->status == 1) {
            return Response::sendData($data->status);
        }
        // 前端驗證失敗
        if ($status == 2) {
            app('model_alipay_auth')->where('status', 0)->where('id', $data->id)->update([
                'status' => $status,
                'update_time' => time(),
            ]);
            return Response::sendData($status);
        }
        // 如果成功 就 進行去驗證查詢
        $result = self::queryFaceCord(UID, [
            'faceid' => $faceid,
            'order_no' => $order_no
        ]);
        $logRtx->log("请求callback返回数据：", date("Y-m-d H:i:s"));
        $logRtx->log("request", $result);
        $logRtx->File();
        $result = json_decode($result, true);
        if ($result['code'] == 0) {
            DB::beginTransaction();
            try {
                $status = 1;
                app('model_alipay_auth')->where('status', 0)->where('id', $data->id)->update([
                    'status' => $status,
                    'update_time' => time(),
                ]);
                app('model_user_auth')->create([
                    'user_id' => $data->user_id,
                    'full_name' => $data->cert_name,
                    'id_number' => $data->cert_no,
                    'app_status' => $status,
                    'created_time' => Carbon::now(),
                    'update_time' => Carbon::now(),
                    'type' => 2
                ]);
                DB::commit();
                return Response::sendData($status);
            } catch (Exception $e) {
                DB::rollBack();
                throw new BasicException(20001);
            }
        }
        return Response::sendData(2);
        // 去查詢是否成功 成功 就 ok


    }

    // 人脸识别查询
    public function queryFaceCord($uid, $param)
    {
        self::getSignticket();
        $signTicket = app('redis')->get(self::$txface_signticket_cache_key);
        if (!$signTicket) {

            throw new BasicException(-1, "接口queryFaceCord,获取SignTicket失败：");
        }
        $nonce = self::getRandomString(32);
        $parms['appId'] = $this->appId;
        $parms['nonce'] = $nonce;
        $parms['orderNo'] = $param['order_no'];
        $parms['ticket'] = $signTicket;
        $parms['version'] = '1.0.0';
        $sign = self::generatesign($parms);
        $parms['sign'] = $sign;
        $paramJson = json_encode($parms);
        //Header头部
        $headers = [
            "Content-Type: application/json; charset=utf-8",
        ];
        $res = self::get_curl_request(self::$api_queryfacerecord_url . $parms['orderNo'], $paramJson, "POST", $headers);
        return $res;
    }


    /**
     * @param $uid
     * @param array $param
     * @return array|bool|string
     * @throws BasicException
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     * 获取签名调用接口
     */
    protected function getFaceId($uid, array $param)
    {
        if (!app('redis')->exists(self::$txface_signticket_cache_key)) {
            self::getSignTicket();
        }
        $signTicket = app('redis')->get(self::$txface_signticket_cache_key);
        if (!$signTicket) {
            throw new BasicException(-1, "接口getFaceId,获取SignTicket失败：");
        }

        $nonce = self::getRandomString(32);

        $parms['webankAppId'] = $this->appId;
        $parms['nonce'] = $nonce;
        $parms['userId'] = $uid;
        $parms['ticket'] = $signTicket;
        $parms['version'] = '1.0.0';

        $sign = self::generatesign($parms);

        $sdkparms['webankAppId'] = $this->appId;
        $sdkparms['nonce'] = $nonce;
        $sdkparms['userId'] = $uid;
        $sdkparms['ticket'] = self::getNonceTicket($uid);
        $sdkparms['version'] = '1.0.0';

        $sdksign = self::generatesign($sdkparms);
        $parms['name'] = $param['cert_name'];
        $parms['idNo'] = $param['cert_no'];
        $parms['orderNo'] = $parms['nonce'];
        $parms['sign'] = $sign;

        $paramJson = json_encode($parms);

        //Header头部
        $headers = [
            "Content-Type: application/json; charset=utf-8",
        ];
        $res = self::get_curl_request(self::$api_getfaceid_url . $parms['orderNo'], $paramJson, "POST", $headers);

        return ['code' => 0, 'sdksign' => $sdksign, 'txparm' => $parms, 'txres' => json_decode($res, true)];
    }

    /**
     * 产生一个指定长度的随机字符串 不长于32位
     * @param int $length
     * @return string
     */
    protected function getRandomString($length = 32)
    {
        $str = '';
        $strPol = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
        for ($i = 0; $i < $length; $i++) {
            $str .= $strPol[mt_rand(0, strlen($strPol) - 1)];
        }
        return $str;
    }

    /**
     * @param $parms
     * @return string
     * 签名使用
     */
    private function generatesign($parms)
    {
        if (gettype($parms) != 'array') {
            return '';
        }
        //对参数字典序排序
        asort($parms, SORT_STRING);
        $str = '';
        foreach ($parms as $k1 => $v1) {
            $str .= $v1;
        }
        return SHA1($str);
    }


    protected function getNonceTicket($uid)
    {
        $logRtx = new LogRepository("tx-auth-request");
        if (app('redis')->exists(self::$txface_accesstoken_cache_key)) {
            // 生效中  access_token
            $access_token = app('redis')->get(self::$txface_accesstoken_cache_key);
        } else {
            // 未生效直重新获取  access_token
            self::getAccessToken();
            $access_token = app('redis')->get(self::$txface_accesstoken_cache_key);
        }
        if (!$access_token) {
            $logRtx->log("获取SignTicketaccess_token失败：", date("Y-m-d H:i:s"));
            $logRtx->File();
            throw new BasicException(-1, "获取SignTicketaccess_token失败：");
        }

        $params = [
            'app_id' => $this->appId,
            'access_token' => $access_token,
            'type' => 'NONCE',
            'version' => '1.0.0',
            'user_id' => $uid,
        ];
        $headers = [];
        $result = self::get_curl_request(self::$api_ticket_url, $params, "get", $headers);
        $result = json_decode($result, true);
        // 结果记录到缓存,记录code不为0时的 tickets['value'] 及对应的有效时间 tickets['expire_in']
        if ($result['code'] != 0) {
            $logRtx->log("请求getNonceTicket报错参数为：", date("Y-m-d H:i:s"));
            $logRtx->log("params",  json_encode($params));
            $logRtx->log("result",  json_encode($result));
            $logRtx->File();
//            Log::info(date("Y-m-d H:i:s") . "请求getNonceTicket报错参数为：" . json_encode($params) . " || 返回参数为：" . json_encode($result));
            throw new BasicException(-1, "获取getNonceTicket失败：" . json_encode($result));
        }
        app('redis')->set(self::$txface_signticket_cache_key, $result['tickets'][0]['value'], 'ex', $result['tickets'][0]['expire_in']);

        return $result['tickets'][0]['value'];
    }


    /**
     * SIGN ticket 获取
     * @throws BasicException
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     */
    protected function getSignTicket()
    {
        $logRtx = new LogRepository("tx-auth-request");
        if (app('redis')->exists(self::$txface_accesstoken_cache_key)) {
            // 生效中  access_token
            $access_token = app('redis')->get(self::$txface_accesstoken_cache_key);
        } else {
            // 未生效直重新获取  access_token
            self::getAccessToken();
            $access_token = app('redis')->get(self::$txface_accesstoken_cache_key);
        }
        if ($access_token) {
            $params = [
                'app_id' => $this->appId,
                'access_token' => $access_token,
                'type' => 'SIGN',
                'version' => '1.0.0',
            ];
            $headers = [];
            $result = self::get_curl_request(self::$api_ticket_url, $params, "get", $headers);
            $result = json_decode($result, true);
            // 结果记录到缓存,记录code不为0时的 tickets['value'] 及对应的有效时间 tickets['expire_in']

            if ($result['code'] != 0) {
                $logRtx->log("请求getSignTicket报错参数为：", date("Y-m-d H:i:s"));
                $logRtx->log("params",  json_encode($params));
                $logRtx->log("result",  json_encode($result));
                $logRtx->File();
//                Log::info(date("Y-m-d H:i:s") . "请求getSignTicket报错参数为：" . json_encode($params) . " || 返回参数为：" . json_encode($result));
                throw new BasicException(-1, "获取SignTicket失败：" . json_encode($result));
            }
            app('redis')->set(self::$txface_signticket_cache_key, $result['tickets'][0]['value'], 'ex', $result['tickets'][0]['expire_in']);
//            $signticket = Cache::store('redis')->get(self::$txface_signticket_cache_key);
//            if(!$signticket){
//                //赋值缓存
//                app('redis')->set(self::$txface_signticket_cache_key,$result['tickets'][0]['value'],$result['tickets'][0]['expire_in']);
//            }
        }
    }

    /**
     * Access Token 获取
     * @throws BasicException
     */
    // Access Token 获取 获取之后立即使用最新的 Access Token。旧的只有一分钟的并存
    protected function getAccessToken()
    {
        $params = [
            'app_id' => $this->appId,
            'secret' => $this->password,
            'grant_type' => 'client_credential',
            'version' => '1.0.0',
        ];
        $logRtx = new LogRepository("tx-auth-request");
        $headers = [];
        $result = self::get_curl_request(self::$access_token_url, $params, "get", $headers);
        $result = json_decode($result, true);
        if ($result['code'] != 0) {
            $logRtx->log("请求getAccessToken报错参数为：", date("Y-m-d H:i:s"));
            $logRtx->log("params",  json_encode($params));
            $logRtx->log("result",  json_encode($result));
            $logRtx->File();
//            Log::info(date("Y-m-d H:i:s") . "请求getAccessToken报错参数为：" . json_encode($params) . " || 返回参数为：" . json_encode($result));
            throw new BasicException(-1, json_encode($result));
        }
        app('redis')->set(self::$txface_accesstoken_cache_key, $result['access_token'], 'ex', $result['expire_in']);
        $logRtx->log("getAccessToken成功：", json_encode($result));
        $logRtx->File();
//        Log::info('getAccessToken成功:' . json_encode($result));
    }


    /**
     * @param $url
     * @param array $param
     * @param string $mothod
     * @param array $headers
     * @param int $return_status
     * @param int $flag
     * @return array|bool|string
     */
    public static function get_curl_request($url, $param = [], $mothod = 'POST', $headers = [], $return_status = 0, $flag = 0)
    {
        $ch = curl_init();
        if (!$flag) {
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        }
        curl_setopt($ch, CURLOPT_TIMEOUT, 6);

        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        if (strtolower($mothod) == 'post') {
            curl_setopt($ch, CURLOPT_POST, true);
            curl_setopt($ch, CURLOPT_POSTFIELDS, $param);
        } else {
            $url = $url . "?" . http_build_query($param);
        }
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 2);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
        #curl_setopt($ch, CURLOPT_PROXY, "127.0.0.1"); //代理服务器地址
        #curl_setopt($ch, CURLOPT_PROXYPORT, 12639); //代理服务器端口
        $ret = curl_exec($ch);
        $code = curl_getinfo($ch);
        curl_close($ch);
        if ($return_status == "1") {
            return array($ret, $code);
        }
        return $ret;
    }

}

